﻿using System;
using System.Collections;
using System.Collections.Generic;

using UnityEngine;
using UnityEngine.EventSystems;
using UnityEngine.UI;

// 棋盘类
public class Board : MonoBehaviour {

    // const
    // 掉落间隔
    private const float FillPieceDuration = 0.2f;
    // 交换拼图的动画间隔
    private const float SwitchPieceCuration = 0.1f;

    // serialize field.
    [SerializeField]
    private GameObject piecePrefab;

    // 动画管理者
    [SerializeField]
    private TweenAnimationManager animManager;

    // private.
    // 棋盘的二维数组(宽和高)
    private Piece [, ] board;

    // 棋盘的宽
    private int width;

    // 棋盘的高
    private int height;

    // 拼图的宽度（根据屏幕分辨率计算得出）
    public int pieceWidth;

    // 随机种子
    private int randomSeed;

    // 拼图周围的所有方向
    private Vector2 [ ] directions = new Vector2 [ ] { Vector2.up, Vector2.down, Vector2.right, Vector2.left };

    // 拼图的下落动画
    private List<AnimData> fillPieceAnim = new List<AnimData> ( );

    // 生成新拼图的时的坐标
    private List<Vector2> pieceCreatePos = new List<Vector2> ( );

    // Unity的事件捕获类
    EventSystem eventSystem;

    /*
    用于检测投向Canvas的射线。Graphic Raycaster既可以配置忽略背面朝上的图形，也可以被UI元素前的2D和3D对象遮挡。
    参数：
        Ignore Reversed Graphics：忽略颠倒的图形的射线检测，即旋转180°后的图片不会与射线发生交互（检测）
        Blocked Objects：会阻挡图形射线的对象类型（2D或(/和)3D对象，需要有collider组件）。
        Blocked Mask：会阻挡图形射线的Layer。
    */
    [SerializeField]
    public GraphicRaycaster RaycastInCanvas;

    [SerializeField]
    private UIManager uiManager;

    //-------------------------------------------------------
    // Public Function
    //-------------------------------------------------------
    // 指定宽高初始化拼图
    public void InitializeBoard (int boardWidth, int boardHeight) {
        width = boardWidth;
        height = boardHeight;

        // 计算拼图的宽度(宽度为屏幕宽度/每一行拼图数)
        pieceWidth = Screen.width / boardWidth;

        board = new Piece [width, height];

        // 遍历宽高 生成所有拼图
        for (int i = 0; i < boardWidth; i++) {
            for (int j = 0; j < boardHeight; j++) {
                CreatePiece (new Vector2 (i, j));
            }
        }

        // 装载下落动画
        animManager.AddListAnimData (fillPieceAnim);
    }

    // 从点击(触摸)输入位置的为止寻找与其相离最近的拼图
    /*public Piece GetNearestPiece (Vector3 input) {
        var minDist = float.MaxValue;
        Piece nearestPiece = null;

        // 输入值与棋盘里拼图的位置进行计算、寻找最近的拼图
        foreach (var p in board) {
            var dist = Vector3.Distance (input, p.transform.position);
            if (dist < minDist) {
                minDist = dist;
                nearestPiece = p;
            }
        }

        return nearestPiece;
    }*/

    // 从点击(触摸)输入位置的为止寻找与其相离最近的拼图
    public Piece GetNearestPiece (Vector3 input) {
        var x = Mathf.Min ((int) (input.x / pieceWidth), width - 1);
        var y = Mathf.Min ((int) (input.y / pieceWidth), height - 1);
        return board [x, y];
    }

    // 寻找第一个点击的拼图
    public Piece GetStartPiece (Vector3 input) {

        // 通过eventSystem生成点击事件
        PointerEventData eventData = new PointerEventData (eventSystem);
        // 赋予点击事件的坐标
        eventData.pressPosition = Input.mousePosition;
        eventData.position = Input.mousePosition;
        // 
        List<RaycastResult> list = new List<RaycastResult> ( );
        RaycastInCanvas.Raycast (eventData, list);
        if (list.Count > 0) {
            for (int i = 0; i < list.Count; i++) {

                // Debug.Log (list [i].gameObject.name);

                if (list [i].gameObject.name.Contains ("Piece")) {
                    return list [i].gameObject.GetComponent<Piece> ( );
                }
            }
        }

        return null;

    }

    // 交换棋盘上的拼图
    public void SwitchPiece (Piece p1, Piece p2) {

        // 移动位置
        // var p1Position = p1.transform.position;
        // p1.transform.position = p2.transform.position;
        // p2.transform.position = p1Position;

        // 两个点之间的动画
        var animList = new List<AnimData> ( );
        animList.Add (new AnimData (p1.gameObject, GetPieceWorldPos (GetPieceBoardPos (p2)), SwitchPieceCuration));
        animList.Add (new AnimData (p2.gameObject, GetPieceWorldPos (GetPieceBoardPos (p1)), SwitchPieceCuration));
        animManager.AddListAnimData (animList);

        // 更新棋盘上的数据
        var p1BoardPos = GetPieceBoardPos (p1);
        var p2BoardPos = GetPieceBoardPos (p2);

        board [(int) p1BoardPos.x, (int) p1BoardPos.y] = p2;
        board [(int) p2BoardPos.x, (int) p2BoardPos.y] = p1;
    }

    // 判断棋盘上是否有配对的拼图
    public bool HasMatch ( ) {
        foreach (var piece in board) {
            if (IsMatchPiece (piece)) {
                return true;
            }
        }
        return false;
    }

    // 删除匹配中的拼图ver.1 (一次性删除全部拼图)
    /*public void DeleteMatchPiece ( ) {
        // 匹配中的拼图全部标记上删除flag
        foreach (var piece in board) {
            piece.deleteFlag = IsMatchPiece (piece);
        }

        // 删除掉被标记了删除flag的拼图
        foreach (var piece in board) {
            if (piece != null && piece.deleteFlag) {
                Destroy (piece.gameObject);
            }
        }
    }*/

    // 删除匹配中的拼图ver.2 (一次性删除全部拼图，有间隔)
    /*public IEnumerator DeleteMatchPiece (Action endCallBadk) {
        // 匹配中的拼图全部标记上删除flag
        foreach (var piece in board) {
            piece.deleteFlag = IsMatchPiece (piece);
        }

        // 删除掉被标记了删除flag的拼图
        foreach (var piece in board) {
            if (piece != null && piece.deleteFlag) {
                Destroy (piece.gameObject);
            }
        }

        yield return new WaitForSeconds (1f);
        endCallBadk ( );
    }*/

    // 删除匹配中的拼图ver.3  (分批次删除配对拼图)
    public IEnumerator DeleteMatchPiece (Action endCallBadk) {
        foreach (var piece in board) {
            if (piece != null && IsMatchPiece (piece)) {
                var pos = GetPieceBoardPos (piece);
                DestroyMatchPiece (pos, piece.GetKind ( ));

                uiManager.AddCombo ( );

                // 遍历下一个间隔0.5秒
                yield return new WaitForSeconds (0.5f);
            }
        }

        endCallBadk ( );
    }

    // 指定的拼图正在配对的场合、将其他配对的拼图同时删除
    private void DestroyMatchPiece (Vector2 pos, PieceKind kind) {
        // 拼图的位置在棋盘之外的时候 停止执行
        if (!IsInBoard (pos)) {
            return;
        }

        // 拼图无效或者被删除的flag、以及种类不同的时候什么也不执行
        var piece = board [(int) pos.x, (int) pos.y];
        if (piece == null || piece.deleteFlag || piece.GetKind ( ) != kind) {
            return;
        }

        // 即使拼图种类相同，如果没有被匹配，什么都不执行
        if (!IsMatchPiece (piece)) {
            return;
        }

        // 删除竖起flag、遍历周围四个方向递归判定
        piece.deleteFlag = true;
        foreach (var dir in directions) {
            DestroyMatchPiece (pos + dir, kind);
        }

        // 删除拼图
        var tweenAlpha = piece.gameObject.AddComponent<AlphaTween> ( );
        tweenAlpha.DoTween (1, 0, 0.3f, ( ) => Destroy (piece.gameObject));
        //Destroy (piece.gameObject);
    }

    // 填满拼图被消失的位置、生成新的拼图
    /*public void FillPiece ( ) {
        // 从下至上遍历棋盘中的拼图
        for (int i = 0; i < width; i++) {
            for (int j = 0; j < height; j++) {
                FillPiece (new Vector2 (i, j));
            }
        }
    }*/

    // 填满拼图被消失的位置、生成新的拼图
    public IEnumerator FillPiece (Action endCallBack) {

        // 初始化动画和位置列表
        fillPieceAnim.Clear ( );
        pieceCreatePos.Clear ( );

        // 从下至上遍历棋盘中的拼图
        for (int i = 0; i < width; i++) {
            for (int j = 0; j < height; j++) {
                FillPiece (new Vector2 (i, j));
            }
        }

        // 播放动画
        animManager.AddListAnimData (fillPieceAnim);

        yield return new WaitForSeconds (1f);
        endCallBack ( );
    }

    // 判断指定的拼图有没有被删除、执行删除之后是进行填充还是生成新的
    private void FillPiece (Vector2 pos) {
        // 当前选择的拼图
        var piece = board [(int) pos.x, (int) pos.y];
        if (piece != null && !piece.deleteFlag) {
            // 拼图如果没被删除，什么也都不发生
            return;
        }
        // 被删除之后的处理:
        // 确认对象拼图的上方向里有没有有効（没被标记删除且没被删除）的拼图、有的话进行移动
        // 上面一格
        var checkPos = pos + Vector2.up;
        // 上一个格子是否在棋盘之中
        while (IsInBoard (checkPos)) {
            // 检查上一格拼图
            var checkPiece = board [(int) checkPos.x, (int) checkPos.y];
            // 如果没有被删除或者被标记删除
            if (checkPiece != null && !checkPiece.deleteFlag) {
                // 让上一格的视觉坐标位置移动到当前格
                checkPiece.transform.position = GetPieceWorldPos (pos);
                // 交换棋盘里的数据
                board [(int) pos.x, (int) pos.y] = checkPiece;
                board [(int) checkPos.x, (int) checkPos.y] = null;
                return;
            }
            checkPos += Vector2.up;
        }

        // 上方没有拼图了进行创建
        CreatePiece (pos);
    }
    //-------------------------------------------------------
    // Private Function
    //-------------------------------------------------------
    // 指定位置生成拼图
    private void CreatePiece (Vector2 position) {

        // 生成后拼图的位置（相对坐标转世界坐标）
        var piecePos = GetPieceWorldPos (position);

        // 生成拼图位置的坐标（相对坐标，最高处）
        var createPos = new Vector2 (position.x, height);

        // 如果该位置已经有拼图了，生成位置向上加一格
        while (pieceCreatePos.Contains (createPos)) {
            createPos += Vector2.up;
        }

        // 登记生成坐标
        pieceCreatePos.Add (createPos);

        // 转成世界坐标
        var pieceCreateWorldPos = GetPieceWorldPos (createPos);

        // 随机决定生成拼图的颜色（种类）
        var kind = (PieceKind) UnityEngine.Random.Range (0, Enum.GetNames (typeof (PieceKind)).Length);

        // 生成拼图、生成后作为棋盘的子对象
        var piece = InstantiatePiece (pieceCreateWorldPos);
        piece.SetSize (pieceWidth);
        piece.SetKind (kind);

        // 登录棋盘的情报
        board [(int) position.x, (int) position.y] = piece;

        // 登录动画
        fillPieceAnim.Add (new AnimData (piece.gameObject, piecePos, FillPieceDuration));
    }

    // 从棋盘上的位置上获取拼图对象的世界坐标
    private Vector3 GetPieceWorldPos (Vector2 boardPos) {
        return new Vector3 (boardPos.x * pieceWidth + (pieceWidth / 2), boardPos.y * pieceWidth + (pieceWidth / 2), 0);
    }

    // 返回拼图在棋盘上处于什么位置
    private Vector2 GetPieceBoardPos (Piece piece) {
        for (int i = 0; i < width; i++) {
            for (int j = 0; j < height; j++) {
                if (board [i, j] == piece) {
                    return new Vector2 (i, j);
                }
            }
        }

        return Vector2.zero;
    }

    // 当前的拼图是否被匹配了
    private bool IsMatchPiece (Piece piece) {
        // 获取拼图的信息
        // 位置
        var pos = GetPieceBoardPos (piece);
        // 种类
        var kind = piece.GetKind ( );

        // 判定纵方向是否匹配中 注: 把自己也要计算进去 所以要加1
        var verticalMatchCount = GetSameKindPieceNum (kind, pos, Vector2.up) + GetSameKindPieceNum (kind, pos, Vector2.down) + 1;

        // 判定横方向是否匹配中 注: 把自己也要计算进去 所以要加1
        var horizontalMatchCount = GetSameKindPieceNum (kind, pos, Vector2.right) + GetSameKindPieceNum (kind, pos, Vector2.left) + 1;

        return verticalMatchCount >= GameManager.MachingCount || horizontalMatchCount >= GameManager.MachingCount;
    }

    /// <summary>
    /// 用参数来计算指定的方向里相同种的拼图有多少个
    /// </summary>
    /// <param name="kind">种类</param>
    /// <param name="piecePos">当前位置</param>
    /// <param name="searchDir">搜索方向</param>
    /// <returns>同种类拼图的个数</returns>
    private int GetSameKindPieceNum (PieceKind kind, Vector2 piecePos, Vector2 searchDir) {
        var count = 0;
        while (true) {
            // 下一个拼图的位置
            piecePos += searchDir;
            if (IsInBoard (piecePos) && board [(int) piecePos.x, (int) piecePos.y].GetKind ( ) == kind) {
                count++;
            } else {
                break;
            }
        }
        return count;
    }

    // 对象的坐标在棋盘中是否存在(有没有出界)
    private bool IsInBoard (Vector2 pos) {

        /*
        // 在行范围中
        bool isInRow = (pos.x >= 0) && (pos.x < width);
        // 在列范围中
        bool isInCol = (pos.y >= 0) && (pos.y < height);
        return isInRow && isInCol;
        */

        return (pos.x >= 0) && pos.y >= 0 && pos.x < width && pos.y < height;
    }

    // 生成拼图对象
    public Piece InstantiatePiece (Vector3 createPos) {
        var piece = Instantiate (piecePrefab, createPos, Quaternion.identity).GetComponent<Piece> ( );
        piece.transform.SetParent (transform);
        return piece;
    }

}
